package edu.stanford.bmir.protege.web.server.stkosutil.cutter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Stack;

import org.protege.editor.owl.model.hierarchy.OWLObjectPropertyHierarchyProvider;
import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.EntityType;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.RemoveAxiom;
import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;

import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.ItemSelector;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.MatchLevel;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.UtilConstants;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingMode;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingScope;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.mapper.MappingOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.mapper.MappingResult;
import edu.stanford.bmir.protege.web.server.owlapi.AssertedClassHierarchyProvider;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProject;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProjectManager;
import edu.stanford.bmir.protege.web.server.owlapi.RenderingManager;
import edu.stanford.bmir.protege.web.server.stkosutil.mapper.OwlClassHolder;
import edu.stanford.bmir.protege.web.server.stkosutil.mapper.OwlClassHolderComparator;
import edu.stanford.bmir.protege.web.server.stkosutil.mapper.OwlPropertyHolder;
import edu.stanford.bmir.protege.web.shared.project.ProjectId;
import edu.stanford.bmir.protege.web.shared.user.UserId;


	public class ObjectPropertyCutter {
			
			 /****
			  *The following functions are copied from OntologyServiceOWLAPIImpl as they are not visible
			  * 
			  */
			 private OWLAPIProject getProject(String projectName) {
			        if (projectName == null) {
			            throw new NullPointerException("projectName must not be null");
			        }
			        ProjectId projectId = ProjectId.get(projectName);
			        // TODO: Log
			        return getProject(projectId);
			    }

			    /**
			     * Gets the OWLAPIProject for a given {@link ProjectId}.If a project with the specified id exists then that project
			     * will be returned, otherwise, a fresh project will be created and that fresh project returned.
			     * @param projectId The id of the project.
			     * @return The OWL API project. Not <code>null</code>.
			     */
			    private OWLAPIProject getProject(ProjectId projectId) {
			        OWLAPIProjectManager pm = OWLAPIProjectManager.getProjectManager();
			        return pm.getProject(projectId);
			    }
			 //*******************   
			
			    
				 private List<OwlPropertyHolder> getSubProperties(OWLAPIProject project, String propertyName) {
				        if (propertyName == null) {
				            return Collections.emptyList();
				        }
				        List<OwlPropertyHolder> result = new ArrayList<OwlPropertyHolder>();
				        RenderingManager rm = project.getRenderingManager();
				        OWLObjectPropertyHierarchyProvider hierarchyProvider = project.getObjectPropertyHierarchyProvider();
				        OWLObjectProperty property = rm.getEntity(propertyName, EntityType.OBJECT_PROPERTY);
				        
				        for (OWLObjectProperty subProperty : new ArrayList<OWLObjectProperty>(hierarchyProvider.getChildren(property))) {
				                String browserText = rm.getBrowserText(subProperty);
				                String name = subProperty.getIRI().toString();
				                OwlPropertyHolder propertyHolder = new OwlPropertyHolder();;
				                propertyHolder.setBrowserText(browserText);
				                propertyHolder.setName(name);
				                propertyHolder.setOwlObjectProperty(subProperty);
				                result.add(propertyHolder);
//				            }
				        }
				        Collections.sort(result, new Comparator<OwlPropertyHolder>() {
				            public int compare(OwlPropertyHolder o1, OwlPropertyHolder o2) {
				                String browserText1 = o1.getBrowserText();
				                String browserText2 = o2.getBrowserText();
				                if(browserText1.startsWith("'")) {
				                    browserText1 = browserText1.substring(1);
				                }
				                if(browserText2.startsWith("'")) {
				                    browserText2 = browserText2.substring(1);
				                }
				                return browserText1.compareToIgnoreCase(browserText2);
				            }
				        });
				        return result;
				    }
				 	private List<OwlPropertyHolder> loadAllPropertyList(OWLAPIProject project, String topPropertyName){
				 		 List<OwlPropertyHolder> result = new ArrayList<OwlPropertyHolder>();
				 		 Stack<OwlPropertyHolder> sPropertyHolders = new Stack<OwlPropertyHolder>();
				 		 OwlPropertyHolder root = this.getTopProperty(project, topPropertyName);
				 		 sPropertyHolders.push(root);
				 		 while (!sPropertyHolders.empty()){
				 			 OwlPropertyHolder currentProperty = sPropertyHolders.pop();
				 			 List<OwlPropertyHolder> children = this.getSubProperties(project, currentProperty.getName());
				 			 Collections.reverse(children);
				 			 for (OwlPropertyHolder child: children){
				 				 sPropertyHolders.push(child);
				 			 }
				 			 result.add(currentProperty);		 
				 		 }
				 		 return result;
				 	}
			 
			 	private OwlPropertyHolder getTopProperty(OWLAPIProject project, String topPropertyName) {
			 	     OwlPropertyHolder holder = new OwlPropertyHolder();
			 	     RenderingManager rm = project.getRenderingManager();
			 	     OWLObjectProperty topProperty = null;
			 	     if (topPropertyName==null){
			 	    	topProperty = project.getDataFactory().getOWLTopObjectProperty();
			 	     }else{
			 	    	topProperty = rm.getEntity(topPropertyName, EntityType.OBJECT_PROPERTY);
			 	     }
			 	     String browserText = rm.getBrowserText(topProperty);
		             String name = topProperty.getIRI().toString();
		             holder.setBrowserText(browserText);
		             holder.setName(name);
			 	     holder.setOwlObjectProperty(topProperty);
			 	     return holder;
			 	}
			 	public CuttingResult cutOntology(String projectName, CuttingOptions options, UserId userId){
			 		String refProjectName = options.getRefProjectName();
			 		CuttingResult result = new CuttingResult();
			 		List<OwlPropertyHolder> oPropertyList = null;
			 	
			 		List<OWLOntologyChange> oChangeList = new ArrayList<OWLOntologyChange>();
			 		
			 		OWLAPIProject currProject = this.getProject(projectName);
			 		//OWLAPIProject refProject = this.getProject(refProjectName);
			 		String topPropertyName = null;
			 		if (options.getCuttingScope()==CuttingScope.SELECTED_PROPERTY_TREE){
			 			topPropertyName = options.getSelectedEntityName();
			 		}
			 		
			 		oPropertyList= this.loadAllPropertyList(currProject, topPropertyName);
			 		int cutNumber = this.fillCuttingFlagAxiom(currProject, oPropertyList, oChangeList,options);
			 		currProject.applyChanges(userId, oChangeList, "本体自动裁切");
			 		result.setCutObjectPropertyNumber(cutNumber);
			 		result.setTotalObjectPropertyNumber(oPropertyList.size());
			 		return result;
			 	}
			 	private int fillCuttingFlagAxiom(OWLAPIProject project, List<OwlPropertyHolder> propertyList, List<OWLOntologyChange> changeList, CuttingOptions options){
			 		int count = 0;
			 		OWLDataFactory df = project.getDataFactory();
			 		for (OwlPropertyHolder owlPropertyHolder: propertyList){
			 			OWLObjectProperty owlProperty = owlPropertyHolder.getOwlObjectProperty(); 
			 			boolean hasMappingNo=false;
			 			
			 			for (OWLAnnotation annotation : owlProperty.getAnnotations(project.getRootOntology(), df.getRDFSComment())) {
				    		 if (annotation.getValue() instanceof OWLLiteral) {
					    		 OWLLiteral commentValue = (OWLLiteral) annotation.getValue();
					    		 String value = commentValue.getLiteral();
					    		  if (value.startsWith(UtilConstants.FLAG_MAPPING_NO)){
					    			  hasMappingNo = true;
					    		  }
					    		  if (value.startsWith(UtilConstants.FLAG_CUTTING)){			  
					    			  OWLAxiom oldAx = df.getOWLAnnotationAssertionAxiom(
					    					  owlProperty.getIRI(), annotation);
						 			  RemoveAxiom removeAxiom = new RemoveAxiom(project.getRootOntology(),oldAx);
						 				 changeList.add(removeAxiom);
					    		  }
					    			  				    		  }
				    	}//end for
			 			
			 			if (qualifiedByCondition(owlPropertyHolder, hasMappingNo, options)){
			 				String annotaionValue =  UtilConstants.FLAG_CUTTING + ": " + "Y"; 
			 				OWLAnnotation commentAnno = df.getOWLAnnotation(
				 					 df.getRDFSComment(),
				 					 df.getOWLLiteral(annotaionValue, "en"));
				 			 OWLAxiom ax = df.getOWLAnnotationAssertionAxiom(
				 					 owlProperty.getIRI(), commentAnno);
				 			 AddAxiom addAxiom =  new AddAxiom(project.getRootOntology(), ax);	 
				 			 changeList.add(addAxiom); 
				 			 count++;
			 			}
			 			
			 		}//end for
			 		return count;	
			 	}
			 	private boolean qualifiedByCondition(OwlPropertyHolder propertyHolder, boolean hasMappingNo, CuttingOptions options){
			 		if (options.getCuttingMode()==CuttingMode.REF_PROJECT){
			 			boolean qualified =  (hasMappingNo && options.getItemSelector()==ItemSelector.MATCHED_ITEMS)
		 					|| (hasMappingNo==false && options.getItemSelector()==ItemSelector.NOT_MATCHED_ITEMS);
			 			return qualified;
			 		}else{
			 			boolean matched = false;
			 			String search = options.getSearch();
			 			String entityName = propertyHolder.getBrowserText();
			 			if (search.compareTo("*")==0){
			 				return options.getItemSelector()==ItemSelector.MATCHED_ITEMS;
			 			}
			 			if (options.getMatchLevel()==MatchLevel.CASE_SENSITIVE){
			 				matched = entityName.compareTo(search)==0;
			 			}else if (options.getMatchLevel()==MatchLevel.CASE_INSENSITIVE){
			 				matched = entityName.compareToIgnoreCase(search)==0;
			 			}else if (options.getMatchLevel()==MatchLevel.INCLUSIVE){
			 				search = search.toLowerCase();
			 				entityName = entityName.toLowerCase();
			 				if (entityName.indexOf(search)!=-1){
			 					matched=true;
			 				}else{
			 					matched=false;
			 				}
			 			}
			 			if (options.getItemSelector()==ItemSelector.MATCHED_ITEMS){
			 				return matched;
			 			}else{
			 				return !matched;
			 			}
			 		}
			 	}
			
			 	
		}
